//
// Created by cjh on 2021/5/13.
//

#include "parser.h"
#include <system_error>
#include <sstream>
#include <iostream>
#include <ctime>
#include <cstring>

using namespace std;


static const char eol[] = "\r\n";
static std::runtime_error invalid_format("http::parse_head error: invalid HTTP format.");


std::string render_head(const http_head& head) { return head.dump(); }

void parse_head(const std::string& str, http_head& ret)
{
    std::stringstream st(str);
    st >> ret.method;
    st >> ret.path;
    st >> ret.version;

    std::string line(200, '\0');
    std::string key, value;
    std::getline(st, line);
    while(st.good()) {
        std::getline(st, line);
        if(line.empty())
            break;

        line.resize(line.size()-1);
        size_t pos = line.find(": ");
        if(pos == std::string::npos)
            throw invalid_format;
        ret.headers.emplace(line.substr(0, pos), line.substr(pos+2));
    }

}

http_head::http_head(http_head &&h)
:method(std::move(h.method)), path(std::move(h.path)), version(std::move(h.version)), headers(std::move(h.headers)) {

}

http_head &http_head::operator=(http_head &&h) {
    method = std::move(h.method);
    path = std::move(h.path);
    version = std::move(h.version);
    headers = std::move(h.headers);
    return *this;
}

std::string http_head::dump()const {
    std::string ret;
    ret += method + ' ' + path + " HTTP/" + version + eol;
    for(auto& val : headers) {
        ret += val.first + ": " + val.second + eol;
    }
    ret += eol;

    return ret;
}

http_head::http_head() :method(10, 0), path(200, 0) {}


std::string render(const std::string& content, int status, const std::string& content_type,
                   std::unordered_map<std::string,std::string> headers,
                   const std::string& http_version)
{
    string ret;
    ret.reserve(4096);
    ret += "HTTP/" + http_version + ' ' + to_string(status) + " OK" + eol;

    time_t t = time(nullptr);
    const char *s = asctime(gmtime(&t));
    headers.emplace("Date", std::string(s, strlen(s)-1) + " GMT");
    headers.emplace("Content-Type", content_type);
    headers.emplace("Content-Length", to_string(content.size()));
    headers.emplace("Connection", "Close");
    headers.emplace("Accept-Ranges", "bytes");
    headers.emplace("Server", "cjh-event-loop-by-epoll");
    for(auto& val : headers) {
        ret += val.first + ": " + val.second + eol;
    }
    ret += eol + content;

    return ret;
}